package biback.utils

import cats.data.{Ior, IorNec}

import scala.reflect.ClassTag

trait DomainObject {
  type CommandResult[R, E] = Either[DomainError, Ior[R, E]]
  type ErrOrNone = Either[DomainError, Unit]
  type ErrOr[P] = Either[DomainError, P]
  type LogErrOr[A] = IorNec[DomainError, A]

  val NONE: ErrOrNone = Right(())

  def orE[A](b: Boolean, a: => A, e: DomainError): Either[DomainError, A] =
    Either.cond(b, a, e)

  def nonNull[A](a: A, e: DomainError): Either[DomainError, A] =
    Either.cond(a != null, a, e)

  def isType[A: ClassTag](a: Any, e: DomainError)(implicit mf: Manifest[A]): Either[DomainError, A] = {
    val clazz = implicitly[ClassTag[A]].runtimeClass
    Either.cond(clazz.isInstance(a), a.asInstanceOf[A], e)
  }

  private val pkgName = "biback.domain."

  def nameOf[A](a: A)(implicit mf: Manifest[A]): String = {
    val name = a.getClass.getName
    if (name.startsWith(pkgName)) name.drop(pkgName.length) else name
  }

  def stringArgs[V](value: sourcecode.Text[V])(implicit enclosing: sourcecode.Enclosing): String =
    enclosing.value + " [" + value.source + "]: " + value.value

  def printArgs[V](value: sourcecode.Text[V])(implicit enclosing: sourcecode.Enclosing): Unit =
    println(enclosing.value + " [" + value.source + "]: " + value.value)

  implicit class AssertEitherOps(b: Boolean) {
    def ?=(e: DomainError): ErrOrNone = Either.cond(b, (), e)
  }

  implicit class OptionEitherOps[A](b: Option[A]) {
    def ?=(e: DomainError): ErrOr[A] = orE(b.nonEmpty, b.get, e)
  }

  implicit def plain2ior[B](b: B): Ior[Unit, B] =
    Ior.right[Unit, B](b)
}
